#include <vector>
#include "opencv2/myopencv.hpp"
#include "TMatrix.h"
#include "LGBPExtractor.h"
#include "SVMPredictor.h"
#include "SVMScaler.h"
#include "FaceGenderPredictor.h"

using namespace std;

FaceGenderPredictor::FaceGenderPredictor(const char* alphaMatFileName, const char* svmModelFileName, const char* rangeFileName,bool saveImage)
:
m_matAlpha( 0 ),
m_svmPredictor( 0 ),
m_scaler( 0 ),
m_resultID(0),
m_saveImage(saveImage),
m_resultDir(0),
m_extractor( 0)
{
	try
	{
		m_matAlpha = new FAST::TMatrix<MappingFeatureType>();
		m_matAlpha->Read( alphaMatFileName, true );

		m_svmPredictor = new SVMPredictor();
		m_svmPredictor->LoadModel( svmModelFileName );

		m_scaler = new SVMScaler( rangeFileName );
	}
	catch(...)
	{
		delete m_matAlpha;
		delete m_svmPredictor;
		throw std::exception( "Create Gender Predictor failed" );
	}

	m_extractor = new LGBPExtractor( 10, 10, m_matAlpha, 0 );
}

FaceGenderPredictor::~FaceGenderPredictor()
{
	delete m_matAlpha;
	delete m_svmPredictor;
	delete m_scaler;
	delete m_extractor;
}

Gender FaceGenderPredictor::Predict(const IplImage *img)
{
	if( 0 == img )
		throw std::exception( "Input image empty" );
	
	IplImage* singleChannelImage = 0;
	if( img->nChannels == 1 )
	{
		singleChannelImage = const_cast<IplImage*>(img);
	}
	else if( img->nChannels == 3 )
	{
		singleChannelImage = cvCreateImage( cvGetSize( img ), 8, 1 );
		singleChannelImage->origin = img->origin;
		cvCvtColor( img, singleChannelImage, CV_BGR2GRAY );
	}
	else
	{
		throw std::exception( "Invalid number of channels" );
	}

	m_extractor->ExtractFeatures( singleChannelImage );
	m_extractor->MakeMappingTestFile();

	vector<double> featureVector;
	const int length = m_extractor->getOutMat()->GetCols();
	featureVector.reserve( length );

	for( int i = 0; i < length; ++i )
	{
		featureVector.push_back( (*(m_extractor->getOutMat()))( 0, i ) );
	}

	m_scaler->Scale( featureVector );

	double label = m_svmPredictor->Predict( featureVector );

	const double epsilon = 0.1;
	Gender gd;
	if( fabs( label - Gender_Female) < epsilon )
	{
		gd = Gender_Female;
	}
	else if( fabs( label - Gender_Male ) < epsilon )
	{
		gd = Gender_Male;
	}
	else
	{
		gd = Gender_Unknown;
	}

	char* GenderString[] = {"Male","FeMale","Unknown", "NoFace"};
	if (m_saveImage && m_resultDir != 0)
	{
		char filename[512];
		sprintf(filename,"%s\\%s_%d.jpg",m_resultDir,GenderString[gd],m_resultID++);
		cvSaveImage(filename,img);
	}

	if( img->nChannels == 3 )
	{
		cvReleaseImage( &singleChannelImage );
		singleChannelImage = 0;
	}

	return gd;
}